package org.ricks.ioc.utils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.Stream;
import java.lang.annotation.Annotation;

public class ClassScanner {

    /**
     * 统一扫描入口：扫描包路径下所有类
     * @param packageName 包名（如 "com.example"）
     * @return 类列表
     */
    public static Set<Class<?>> scanPackage(String packageName) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace('.', '/');
        try {
            Enumeration<URL> resources = classLoader.getResources(path);
            Set<Class<?>> classes = new HashSet<>();

            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();
                switch (resource.getProtocol()) {
                    case "file":
                        classes.addAll(scanFileSystem(Paths.get(resource.toURI()), packageName, classLoader));
                        break;
                    case "jar":
                    case "resource":
                        classes.addAll(scanJarResource(resource, packageName, classLoader));
                        break;
                    default:
                        System.err.println("Unsupported protocol: " + resource.getProtocol());
                }
            }
            return classes;
        } catch (Throwable throwable) {
            throw new RuntimeException("scan package error , packageName:" + packageName, throwable);
        }
    }

    /**
     * 扫描文件系统目录中的类
     */
    private static List<Class<?>> scanFileSystem(Path baseDir, String basePackage, ClassLoader classLoader) throws IOException {
        List<Class<?>> classes = new ArrayList<>();
        if (!Files.isDirectory(baseDir)) return classes;

        Files.walkFileTree(baseDir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                if (file.toString().endsWith(".class") && !file.toString().contains("$")) {
                    loadClassFromPath(file, baseDir, basePackage, classLoader).ifPresent(classes::add);
                }
                return FileVisitResult.CONTINUE;
            }
        });
        return classes;
    }

    /**
     * 扫描JAR包中的类
     */
    private static List<Class<?>> scanJarResource(URL resource, String basePackage, ClassLoader classLoader)
            throws IOException, URISyntaxException {

        List<Class<?>> classes = new ArrayList<>();
        URI uri = resource.toURI();
        try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) {
            Path basePath = fs.getPath(basePackage.replace('.', '/'));
            if (Files.notExists(basePath)) return classes;

            try (Stream<Path> walk = Files.walk(basePath)) {
                walk.filter(Files::isRegularFile)
                        .filter(path -> path.toString().endsWith(".class"))
                        .forEach(path ->
                                loadClassFromPath(path, basePath, basePackage, classLoader).ifPresent(classes::add)
                        );
            }
        } catch (FileSystemAlreadyExistsException e) {
            // 复用已存在的FileSystem
            try (FileSystem fs = FileSystems.getFileSystem(uri)) {
                Path basePath = fs.getPath(basePackage.replace('.', '/'));
                try (Stream<Path> walk = Files.walk(basePath)) {
                    walk.filter(Files::isRegularFile)
                            .filter(path -> path.toString().endsWith(".class"))
                            .forEach(path ->
                                    loadClassFromPath(path, basePath, basePackage, classLoader).ifPresent(classes::add)
                            );
                }
            }
        }
        return classes;
    }

    /**
     * 从路径加载类（核心逻辑）
     */
    private static Optional<Class<?>> loadClassFromPath(Path classPath, Path basePath, String basePackage, ClassLoader classLoader) {
        try {
            String relativePath = basePath.relativize(classPath).toString();
            String className = basePackage + "." +
                    relativePath.replace("/", ".")
                            .replace("\\", ".")
                            .replace(".class", "");

            // 仅定义类而不初始化（避免触发静态代码块和依赖加载）
            return Optional.of(Class.forName(className, false, classLoader));
        } catch (ClassNotFoundException e) {
            System.err.println("Class not found: " + classPath);
            return Optional.empty();
        } catch (NoClassDefFoundError e) {
            // 捕获缺失依赖错误
            Logger.debug("Skipping class due to missing dependency: {}" , classPath);
            Logger.debug("Missing class: {}" ,e.getMessage());
            return Optional.empty();
        } catch (Throwable e) {
            Logger.error("Unexpected error loading class: {}", e,   classPath);
            return Optional.empty();
        }
    }

    /**
     * 扫描带注解的类
     */
    public static Map<Class<? extends Annotation>, List<Class<?>>> findAnnotatedClasses(
            List<Class<? extends Annotation>> annotations, String packageName) throws Exception {

        Set<Class<?>> classes = scanPackage(packageName);
        Map<Class<? extends Annotation>, List<Class<?>>> result = new HashMap<>();

        for (Class<?> clazz : classes) {
            for (Class<? extends Annotation> annotation : annotations) {
                if (clazz.isAnnotationPresent(annotation)) {
                    result.computeIfAbsent(annotation, k -> new ArrayList<>()).add(clazz);
                }
            }
        }
        return result;
    }


    public static void main(String[] args) throws Exception {
        Set<Class<?>> classes = ClassScanner.scanPackage("org.ricks");
        System.err.println("扫描出来了class文件 len:" + classes.size());
    }
}
